package priv.pfz.qatool;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.mashape.unirest.http.Unirest;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import priv.pfz.qatool.annotation.QAToolMethod;
import priv.pfz.qatool.annotation.QAToolName;
import priv.pfz.qatool.annotation.QAToolParam;
import priv.pfz.qatool.enums.QAToolMethodType;
import priv.pfz.qatool.enums.QAToolParamType;
import priv.pfz.qatool.model.QT;
import priv.pfz.qatool.model.QTMethod;
import priv.pfz.qatool.model.QTParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;


/**
 * Created by fangzhou.peng on 2017/8/11.
 */
public class QATool {

    private static final Logger logger = Logger.getLogger(QATool.class);
    private static final ThreadLocal<QTContext> ctx = new ThreadLocal<QTContext>();

    public static String apply(Object obj, HttpServletRequest request, HttpServletResponse response, Writer out) {
        QT qt;
        try {
            qt = toQT(obj);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return returnJson("parse jsp error", getStackTrace(e));
        }
        QTContext context = new QTContext(qt, request, response, out);
        ctx.set(context);
        if (context.isInvoke()) {
            return invoke();
        } else {
            return info();
        }
    }

    public static void print(Object obj) {
        QTContext context = ctx.get();
        if (context.qt().getMethod(context.method()).getType() != QAToolMethodType.PRINT) {
            return;
        }
        Writer out = context.out();
        try {
            out.write(String.valueOf(obj));
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void printLine(Object obj) {
        print(obj);
        print("<br/>");
    }

    public static void printLine() {
        print("<br/>");
    }

    public static QTContext getContext() {
        return ctx.get();
    }

    private static String returnJson(String info, String detail) {
        ctx.get().response().setContentType("application/json; charset=utf-8");
        JSONObject json = new JSONObject();
        json.put("info", info);
        json.put("detail", detail);
        return json.toString();
    }

    private static String getStackTrace(Exception e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        pw.close();
        return sw.toString();
    }

    private static QT toQT(Object obj) throws IllegalAccessException {
        QT qt = new QT();
        Class clazz = obj.getClass();

        qt.setObj(obj);
        qt.setClassName(clazz.getSimpleName().replace("_jsp", ""));
        for (Field field : clazz.getDeclaredFields()) {
            if (field.getAnnotation(QAToolName.class) != null) {
                field.setAccessible(true);
                qt.setName(field.get(obj).toString());
            }
        }

        for (Method method : clazz.getDeclaredMethods()) {
            QAToolMethod qaToolMethod = method.getAnnotation(QAToolMethod.class);
            if (qaToolMethod != null) {
                method.setAccessible(true);
                QTMethod qtMethod = new QTMethod();
                qtMethod.setMethod(method);
                qtMethod.setMethodName(method.getName());
                qtMethod.setType(qaToolMethod.type());
                qtMethod.setName(qaToolMethod.name());
                qtMethod.setDescs(Lists.newArrayList(qaToolMethod.descs()));
                qtMethod.setAuth(qaToolMethod.auth());
                qtMethod.setApi(qaToolMethod.api());
                if (qaToolMethod.type() == QAToolMethodType.EXCEL && method.getReturnType() != HSSFWorkbook.class) {
                    throw new IllegalArgumentException("method type EXCEL must use HSSFWorkbook as return type");
                }

                Class[] paramClasses = method.getParameterTypes();
                Annotation[][] annotations = method.getParameterAnnotations();
                for (int i = 0; i < paramClasses.length; i++) {
                    Class paramClass = paramClasses[i];
                    QTParam qtParam = new QTParam();
                    qtParam.setClazz(paramClass);
                    qtParam.setIndex(i);
                    for (Annotation annotation : annotations[i]) {
                        if (annotation.annotationType() == QAToolParam.class) {
                            QAToolParam qaToolParam = (QAToolParam) annotation;
                            qtParam.setDefaultValue(qaToolParam.defaultValue());
                            qtParam.setType(qaToolParam.type());
                            qtParam.setDesc(qaToolParam.desc());
                            qtParam.setName(qaToolParam.name());
                            qtParam.setSelectOptions(Lists.newArrayList(qaToolParam.selectOptions()));
                            break;
                        }
                    }
                    if ((qtParam.getType() == QAToolParamType.DATE_PICKER || qtParam.getType() == QAToolParamType.DATE_TIME_PICKER)
                            && !(qtParam.getClazz() == long.class || qtParam.getClazz() == Long.class)) {
                        throw new IllegalArgumentException("param type DATE_TIME_PICKER or DATE_PICKER must be long or Long");
                    }
                    if (qtParam.getType() == QAToolParamType.CHECKBOX && !(qtParam.getClazz() == boolean.class || qtParam.getClazz() == Boolean.class)) {
                        throw new IllegalArgumentException("param type CHECKBOX must be boolean or Boolean");
                    }
                    if (qtParam.getType() == QAToolParamType.FILE && qtParam.getClazz() != File.class) {
                        throw new IllegalArgumentException("param type FILE must be File");
                    }
                    qtMethod.addParam(qtParam);
                }

                qt.addMethod(qtMethod);
            }
        }
        return qt;
    }

    private static String info() {
        return returnJson("info", JSON.toJSONString(ctx.get().qt()));
    }

    private static String invoke() {
        QTContext context = ctx.get();
        QT qt = context.qt();
        String methodName = context.method();
        QTMethod qtMethod = qt.getMethod(methodName);

        //checkAuth
//        String userName = HeRuntimeContext.getContext().get(AnimalConstans.CTX_KEY_SSO_USER);
//        if (!checkAuth(userName, qtMethod.getAuth())) {
//            String msg = String.format("access denied, userName=%s, methodName=%s, auth=%s", userName, qtMethod.getMethodName(), qtMethod.getAuth());
//            logger.error(msg, new IllegalArgumentException());
//            return returnJson("拒绝访问", "您没有权限进行此操作，请联系后端~");
//        }

        List<QTParam> params = qtMethod.getParams();
        Object[] objects = new Object[params.size()];
        try {
            for (int i = 0; i < params.size(); i++) {
                QTParam param = params.get(i);
                objects[i] = context.get(i, param);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return returnJson("参数错误", getStackTrace(e));
        }
        Method method = qtMethod.getMethod();
        if (qtMethod.getType() == QAToolMethodType.EXCEL) {
            try {
                HSSFWorkbook excel = (HSSFWorkbook) method.invoke(qt.getObj(), objects);
                String excelName = qtMethod.getMethodName() + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                context.response().setContentType("octets/stream");
                context.response().addHeader("Content-Disposition", "attachment;filename=" + new String(excelName.getBytes("utf-8"), "ISO8859-1") + ".xls");
                context.response().setCharacterEncoding("UTF-8");
                OutputStream out = context.response().getOutputStream();
                excel.write(out);
                out.close();
                return returnJson("成功", "");
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return returnJson("下载失败", getStackTrace(e));
            }
        } else if (qtMethod.getType() == QAToolMethodType.PRINT) {
            try {
                method.invoke(qt.getObj(), objects);
                return "";
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return returnJson("调用失败", getStackTrace(e));
            }

        } else {
            try {
                Object result = method.invoke(qt.getObj(), objects);
                return returnJson("成功", Optional.fromNullable(result).or("").toString());
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return returnJson("调用失败", getStackTrace(e));
            }
        }
    }

//    public static boolean checkAuth(String userName, QAToolMethodAuth auth) {
//        try {
//            //dev全放开
//            if (AnimalContext.isDevEnv()) {
//                return true;
//            }
//            //DEV级：线上不可用
//            if (auth == QAToolMethodAuth.DEV) {
//                return false;
//            }
//            EntriesConfig config = ConfigManager.get(EntriesConfig.class, "auth");
//            Set<String> admins = StringUtil.splitStringToSet(config.getEntryBy("key", "admin").optString("value"), ",");
//            Set<String> others = StringUtil.splitStringToSet(config.getEntryBy("key", "qa_tool").optString("value"), ",");
//            if (admins.contains(userName)) {
//                return true;
//            }
//            if (others.contains(userName) && auth == QAToolMethodAuth.QA) {
//                return true;
//            }
//            return false;
//        } catch (Exception e) {
//            logger.error(e.getMessage(), e);
//            return false;
//        }
//    }

    public static void warmJsp() {
        try {
            //Unirest.post("http://127.0.0.1/sso/qa_tool.jsp").queryString("secret", SSOAuthFilter.secret).asStringAsync();
            Unirest.post("http://127.0.0.1:8080/qatool/qa_tool.jsp").asStringAsync();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
}
